之前在第 15 天有跟各位捧油們提到過 物件 在實務上出場次數之頻繁,是必需要去了解的一塊;那篇底下也有貼出阿宅 PO 很常拜讀的幾篇大師的文章,非常推薦新手們服用!
今天要聊的主題 -- 深拷貝、淺拷貝,就是與 JavaScript 物件 (Object)
有相關喔!
之前有提過 JavaScript 的型別有分為兩種 -- 基本型別、物件
這兩者在傳值
行為上有很大的不同,怎麼說呢?
來~我們來看幾段程式碼:
let number01 = 520;
let number02 = number01;
console.log(number02);// 520
number01 = 1314;
console.log(number01);// 1314
console.log(number02);// 520
在上面這段程式碼中可以看到,number02 的值一開始是複製 number01 的值,所以 console.log 出來的值是 520,這邊沒有問題~
而後面我們在另外賦新的值給 number01 之後再 console.log 出來看看,number01 的值已改成新的值,但 number02 的值並 沒有改變
!
原因是因為在 JavaScript 的 基本型別 裡的傳值方式是 Call by Value
number02 單純複製了 number01 的值
罷了,所以當我們在修改 number01 的值的時並不會修改到 number02 的值。 就是像我們在打文章的 Copy、Paste
但是在 物件 上的實作,情況可就大大的不同囉~~
一樣按照上面的複製的情境來一次,但對象改成 物件
let object01 = {a:"Jack", b:"Anne"};
let object02 = object01;
console.log(object02);// {a:"Jack", b:"Anne"}
object01.a = "Peter";
console.log(object01);// {a:"Peter", b:"Anne"}
console.log(object02);// {a:"Peter", b:"Anne"}
想必眼睛雪亮亮的觀眾們一定發現了詭異之處...
那就是,為什麼我重新賦值給 object01 但 object02 的值卻也跟著改變了!?
那是因為在 JavaScript Object 的傳值方式是 Call by Reference
什麼是 Reference ? 意思就是你複製出來的另一個物件,他們共用一個參考,也就是共用一個記憶體位置
。
這邊用圖片解釋會更清楚
從上圖我們可以看到,object01 跟 object02 他們都是指向同一個記憶體位置,這個記憶體內容放著 {a:"Jack", b:"Anne"} 這個資料,當我們修改無論是 object01 OR object02,實際上都是改到同一個記憶體內的物件資料!
那我們如果希望可以像第一種基本型別的複製資料的方式,使用在物件上呢?
阿宅 PO 收集了以下的幾種方法:
let object01 = {a:"Jack", b:"Anne"};
let object02 = {a:object01.a, b:object01.b};
object01.a = "Peter";
console.log(object01);// {a:"Peter", b:"Anne"}
console.log(object02);// {a:"Jack", b:"Anne"}
let object01 = {a:"Jack", b:"Anne"};
let object02 = Object.assign({}, object01);
object01.a = "Peter";
console.log(object01.a)// "Peter"
console.log(object02.a)// "Jack"
let object01 = {a:"Jack", b:"Anne"};
let object02 = {...object01};
object01.a = "Peter";
console.log(object01.a)// "Peter"
console.log(object02.a)// "Jack"
以上這 3 個方式只能用在於 只有一層的物件資料格式
所以才會叫做 淺拷貝
當資料內是 物件內又包物件 的時候,這 3 個方式在資料的第二層以上的內容,又會回到 Call by Reference
的情形囉~
let object01 = {a:"Jack", b:{c:"Anne"}};
let object02 = {...object01}; // 實作上方方法來操作 兩層物件
let transJSON = JSON.parse(JSON.stringify(object01));
object01.a = "Peter"
object01.b.c = "Mary"
console.log(object01);// {a:"Peter", b:{c:"Mary"}};
console.log(object02);// {a:"Jack", b:{c:"Mary"}}; 第二層物件依然被改變
console.log(transJSON);// {a:"Jack", b:{c:"Anne"}}; 完美複製
Tips: 要 依序
先 JSON.stringify 再 JSON.parse
let object01 = {a:"Jack", b:{c:"Anne"}};
let object02 = _.cloneDeep(object01)
object01.b.c = "Mary"
console.log(object01);// {a:"Jack", b:{c:"Mary"}};
console.log(object02);// {a:"Jack", b:{c:"Anne"}}; 完美複製